﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Practices.Unity;
using MiniStock.BusinessLogic.EventProcessor.EntityCache;
using MiniStock.BusinessLogic.EventProcessor.EventRepository;
using MiniStock.BusinessLogic.EventQueue.Queue;
using MiniStock.Entities;
using MiniStock.Entities.Common;
using MiniStock.Entities.Common.Events;
using MiniStock.Infrastructure;
using System.Diagnostics;
using MiniStock.Infrastructure.Configurator;
using MiniStock.Infrastructure.Queue;

namespace MiniStock.BusinessLogic.EventProcessor
{
    public class EventProcessorWorker : QueuedEventWorkerBase
    {
        [Dependency]
        public EntityCacheBase EntityCache { get; set; }

        [Dependency]
        public IRepository<EventProcessorEventData> EventRepository { get; set; }

        public override void InitWork()
        {
            base.InitWork();
            EntityCache.Initialize();
        }

        public override void ProcessMessage(EntityEventBase message)
        {
            try
            {
                EntityBase entity = null;

                // Process message
                this.EntityCache.TryGetItemByHash(message.GetEntityHash(), out entity);
                message.ValidateFor(entity, this.EntityCache);

                // save event to event log
                this.EventRepository.Add(message);
                this.EventRepository.Commit();

                // for construction messages, construct a new entity
                if (message.IsConstructionMessage)
                {
                    entity = (EntityBase)RootContainer.Resolve(message.GetEntityType());
                }

                message.ApplyTo(entity);

                // after message construction, save the initial snapshot too
                if (message.IsConstructionMessage)
                {
                    this.EventRepository.Add(new Snapshot
                    {
                        EventId = Guid.NewGuid(),
                        CorrelatedEventId = message.EventId,
                        EventDate = DateTime.Now,
                        Entity = entity,
                    });
                    this.EventRepository.Commit();
                }

                Debug.Assert(message.GetEntityHash() == entity.GetEntityHash(), "EntityHash mismatch");
                this.EntityCache.AddEntity(entity);

                var result = message.GetCorrelatedResultEvent<MessageProcessedEvent>(entity);
                result.SenderAddress = this.Name;
                OutputEventQueue.Enqueue(result);
            }
            catch(ValidationException ex)
            {
                var result = message.GetCorrelatedResultEvent<MessageFaultedEvent>(null);
                result.ErrorMessage = ex.Message;
                result.SenderAddress = this.Name;
                OutputEventQueue.Enqueue(result);
            }
            catch (Exception ex)
            {
                Trace.TraceError("{0}", ex);
            }
        }
    }
}
